﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;

namespace LibIpMsgNet
{
    
    public class TRecvBuffer
    {
        public IPEndPoint EndPoint;
        public int Size;
        public byte[] Buffer;

        public TRecvBuffer()
        {
            EndPoint = new IPEndPoint(IPAddress.Any, 65535);
            Buffer = new byte[Consts.MAX_UDPBUF];
        }
    }

    public class TUdpReceivedArgs : EventArgs
    {
        public TRecvBuffer Buffer;

        public TUdpReceivedArgs(TRecvBuffer buf)
        {
            this.Buffer = buf;
        }

        public TUdpReceivedArgs() { }
    }

    public delegate void TUdpReceivedHandle(object sender, TUdpReceivedArgs args);

    public class TConnection
    {
        private Socket udp_sd = null;
        private Socket tcp_sd = null;

        private ulong packetNo;
        private bool status;
        private THostSub local;

        public event TUdpReceivedHandle UdpReceived;

        public bool Status
        {
            get
            {
                return status;
            }
        }

        public THostSub LocalHost
        {
            get
            {
                return local;
            }
        }
        
        public TConnection(IPEndPoint EndPoint)
        {
            status = false;

            local = new THostSub();
            local.EndPoint = EndPoint;
            local.HostName = Environment.MachineName;
            local.UserName = Environment.UserName;

            packetNo = (ulong)DateTime.Now.Ticks;

            if (!InitSocket())
                return;

            status = true;
        }

        public void Close()
        {
            CloseSocket();
        }

        private bool InitSocket()
        {
            try
            {
                udp_sd = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                tcp_sd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);

                udp_sd.Bind(local.EndPoint);
                tcp_sd.Bind(local.EndPoint);

                udp_sd.Blocking = false;
                udp_sd.EnableBroadcast = true;
                udp_sd.SendBufferSize = Consts.MAX_SOCKBUF;
                udp_sd.ReceiveBufferSize = Consts.MAX_SOCKBUF;

                tcp_sd.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

                tcp_sd.Listen(5);

                return true;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.StackTrace + ex.Message);
                return false;
            }
        }

        private void CloseSocket()
        {
            if (udp_sd != null)
            {
                udp_sd.Shutdown(SocketShutdown.Both);
                udp_sd.Close();
                udp_sd = null;
            }

            if (tcp_sd != null)
            {
                tcp_sd.Shutdown(SocketShutdown.Both);
                tcp_sd.Close();
                tcp_sd = null;
            }
        }

        private bool ResetSocket()
        {
            CloseSocket();
            return InitSocket();
        }

        public ulong MakePacketNo()
        {
            return packetNo++;
        }

        public bool UdpSend(IPEndPoint EndPoint, byte[] Buffer, int Len)
        {
            try
            {
                udp_sd.SendTo(Buffer, Len, SocketFlags.None, EndPoint);

                return true;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.StackTrace + ex.Message);
                return false;
            }
        }

        public void UdpRecv()
        {
            try
            {
                TRecvBuffer Buffer = new TRecvBuffer();

                EndPoint ep = (EndPoint)Buffer.EndPoint;

                System.Collections.ArrayList socketList = new System.Collections.ArrayList(1);
                socketList.Add(udp_sd);
                Socket.Select(socketList, null, null, 100);

                if (socketList.Count > 0)
                {
                    for (int i = 0; i < socketList.Count; i++)
                    {
                        Socket sk = (Socket)socketList[i];
                        Buffer.Size = sk.ReceiveFrom(Buffer.Buffer, Consts.MAX_NAMEBUF - 1, SocketFlags.None, ref ep);

                        Buffer.EndPoint.Address = ((IPEndPoint)ep).Address;
                        Buffer.EndPoint.Port = ((IPEndPoint)ep).Port;

                        Buffer.Buffer[Buffer.Size] = 0;
                    }

                    if (UdpReceived!=null)
                        UdpReceived(this, new TUdpReceivedArgs(Buffer));
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.StackTrace + ex.Message);
            }
        }

        public void UdpSendAsync()
        {
        }

        private void UdpRecvAsync(IAsyncResult iar)
        {
            try
            {
                //EndPoint ep = (EndPoint)Buffer.EndPoint;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.StackTrace + ex.Message);
            }
        }

    }
}
